-- 登录服务主节点
-- Author: Colocasia
-- Date: 2019-05-01

local skynet = require "skynet";
require "skynet.manager";
local cluster = require "skynet.cluster";
local socket = require "us_socket";
local log = require "log";
local table = table;
local string = string;
local assert = assert;

local SEND_SESSION_NO_RET = 0

local loginconf =
{
    name = "loginservice",
    host = skynet.getenv "login_host",
    port = tonumber(skynet.getenv "login_port"),
}

local loginservice = {}

-- master处理
-- Socket
local socket_ipv4 = nil;
local socket_ipv6 = nil;

-- 连接信息
local connectionlist = {}

-- slave列表
local slavelist = {}
local curblance = 1;

-- gate列表
local server_list = {};

-- 消息处理
local handler = {}

function handler.on_connect(fd, addr)
    log.printf("handler open");
    local s = slavelist[curblance];
    connectionlist[fd] = s;
    curblance = curblance + 1;
    if curblance > #slavelist then
        curblance = 1;
    end
    skynet.send(s, "lua", "connect", fd, addr);
end

function handler.on_message(fd, msg, sz)
    log.printf("handler data");

    local s = connectionlist[fd];
    if s then
        -- 客户端的消息 进行转发
        -- skynet.send(s, "lua", "message", fd, msg, sz);
        skynet.redirect(s, fd, "client", fd, msg, sz)
    else
        log.warning("[Login]Don't have any slave handler this client fd->%s, msg->%s", fd, skynet.tostring(msg, sz));
        -- skynet.tostring will copy msg to a string, so we must free msg here.
        skynet.trash(msg, sz)
        return false
    end
end

function handler.on_disconnect(fd)
    loginservice.close_client(fd);
end

function handler.on_error(fd, msg)
    log.info(msg);
    return handler.on_disconnect(fd);
end

-- 打开一个连接
function loginservice.open_client(fd)
    if connectionlist[fd] then
        socket.start(fd);
        return true
    end
    return false
end

-- 关闭一个连接
function loginservice.close_client(fd)
    local s = connectionlist[fd];
    if s then
        skynet.send(s, "lua", "disconnect", fd);
    end

    if connectionlist[fd] then
        connectionlist[fd] = nil;
        socket.close(fd);
        return true
    end
    return false
end

-- 同步server_list到slaved
local function sync_server_list_to_slaved()
    -- 通知所有slaved
    for _, s in pairs(slavelist) do
        skynet.send(s, "lua", "update_server_list", server_list);
    end
end

-- 检查是否存在指定名称的slaved
local function check_exist_slaved(gate)
    for _, s in ipairs(server_list) do
        if s.node == gate.node and s.name == gate.name then
            return true;
        end
    end
    return false;
end

-- 注册gate节点
function loginservice.register_gate(gate)
    -- 如果重复添加节点
    if check_exist_slaved(gate) then
        log.warning("[Login]Regist an not exsit server!");
        return;
    end

    if gate.node then
        local gate = cluster.query(gate.node, "gate");
        local proxy = cluster.proxy(gate.node, gate);
        gate.proxy = proxy;
    end

    -- 节点添加进列表
    table.insert(server_list, gate);
    -- 通知所有节点
    sync_server_list_to_slaved();
end

-- 注销gate
function loginservice.remove_gate(gate)
    -- 如果删除不存在的节点
    if not check_exist_slaved(gate) then
        log.warning("[Login]Remove an exsit server!");
        return;
    end

    -- 移除节点
    local index;
    for i, s in ipairs(server_list) do
        if s.node == gate.node and s.name == gate.name then
            index = i;
            break;
        end
    end

    -- 从节点列表中移除
    table.remove(server_list, index);
    -- 通知所有节点
    sync_server_list_to_slaved();
end

-- 错误处理
function on_cmd_err(msg)
    log.error(msg);
end

-- 初始化
function loginservice.lanch_master()
    -- print("=====>lanch_master<=====");
    -- 注册master服务名字
    local servicename = "."..(loginconf.name or "loginservice");
    skynet.register(servicename);

    skynet.dispatch("lua", function(session, source, cmd, ...)
        -- log.info("master dispatch source is %s! command is %s!", source, command);
        local f = assert(loginservice[cmd], "loginservice cmd=>"..cmd.." not found");
        local function ret(ok, ...)
            if session ~= SEND_SESSION_NO_RET then
                skynet.retpack (...)
            end
        end
        ret(xpcall(f, on_cmd_err, ...));
    end)

    -- 创建salve节点
    local instance = loginconf.instance or 8;
    for i=1, instance do
        -- table.insert(slavelist, skynet.newservice(SERVICE_NAME, skynet.self()));
        table.insert(slavelist, skynet.newservice("us_login_slaved", skynet.self()));
    end

    local blance = 1;
    local host = loginconf.host or "0.0.0.0";
    local port = assert(tonumber(loginconf.port), "login server invalid listen port");

    log.info("login server listen at addr -> %s:%d", host, port);
    socket.register(handler);

    -- ipv4
    socket_ipv4 = socket.listen(host, port);
    socket.start(socket_ipv4);
    -- log.printf("login server socketid is %s", socketid);
    
    -- ipv6
    socket_ipv6 = socket.listen("::", port);
    socket.start(socket_ipv6);
    -- log.printf("login server socketid is %s", socketid);
end

-- 关闭login
function loginservice.close_master()
    if socket_ipv4 then
        socketdriver.close(socket_ipv4)
        socket_ipv4 = nil;
    end
    if socket_ipv6 then
        socketdriver.close(socket_ipv6)
        socket_ipv6 = nil;
    end
end

-- 注册协议
skynet.register_protocol {
	name = "client",
	id = skynet.PTYPE_CLIENT,
}
skynet.start(loginservice.lanch_master)

return loginservice;
